
#include <stdio.h>

#include "els.h"
#include "els_heap.h"
#include "els_gc.h"
#include "els_lex.h"
#include "els_mem.h"
#include "els_vmhost.h"
#include "els_string.h"
#include "els_unit.h"

#define api_incr_top(L)	incr_top
static void els_newunit(els_VmObj *L) {
  hvalue(L->top) = els_Unit_new(L, 0);
  ttype(L->top) = ELS_TYPE_UNIT;
  api_incr_top(L);
}

static int els_heap_ref (els_VmObj *L,  int lock) {
  int ref;
  if (ttype(L->top-1) == ELS_TYPE_NULL)
    ref = ELS_REFNULL;
  else {
    if (L->refFree != NONEXT) {  
      ref = L->refFree;
      L->refFree = L->refArray[ref].st;
    }
    else {  
      els_Mem_growvector(L, L->refArray, L->refSize, 1, struct Ref,"初始化单元溢出", MAX_INT);
      L->nblocks += sizeof(struct Ref);
      ref = L->refSize++;
    }
    L->refArray[ref].o = *(L->top-1);
    L->refArray[ref].st = lock ? LOCK : HOLD;
  }
  L->top--;
  return ref;
}

static void init_entry(els_VmObj *L, int tag)
{
    int i;
    for (i = 0; i < TM_N; i++)
        els_unit_gettm(L, tag, i) = NULL;
    L->UNITSEC[tag].collected = NULL;
}

void els_unit_init(els_VmObj *L)
{
    int t;
    els_Mem_growvector(L, L->UNITSEC, 0, NUM_TAGS, struct TM, "", MAX_INT);
    L->nblocks += NUM_TAGS * sizeof(struct TM);
    L->last_tag = NUM_TAGS - 1;
    for (t = 0; t <= L->last_tag; t++)
        init_entry(L, t);
}

int els_unit_tag(const StackObj *o)
{
    int t = ttype(o);
    switch (t)
    {
    case ELS_TYPE_USERDATA:
        return tsvalue(o)->u.d.tag;
    case ELS_TYPE_UNIT:
        return hvalue(o)->htag;
    default:
        return t;
    }
}

static void f_elsopen(els_VmObj *L, void *ud)
{
    int stacksize = *(int *)ud;
    if (stacksize == 0)
        stacksize = DEFAULT_STACK_SIZE;
    else
        stacksize += ELS_MINSTACK;
    L->gt = els_Unit_new(L, 10);
    els_Heap_init(L, stacksize);
    els_string_init(L);
    els_lexer_init(L);
    els_unit_init(L);
    els_newunit(L);
    els_heap_ref(L, 1);
}

ELS_API els_VmObj *els_createvm(int stacksize)
{
    els_VmObj *L = els_Mem_new(NULL, els_VmObj);
    if (L == NULL)
        return NULL;
    L->stack = NULL;
    L->strt.size = L->udt.size = 0;
    L->strt.nuse = L->udt.nuse = 0;
    L->strt.hash = NULL;
    L->udt.hash = NULL;
    L->Mbuffer = NULL;
    L->Mbuffsize = 0;
    L->rootcodeir = NULL;
    L->rootcl = NULL;
    L->rootunit = NULL;
    L->UNITSEC = NULL;
    L->last_tag = -1;
    L->refArray = NULL;
    L->refSize = 0;
    L->refFree = NONEXT;
    L->nblocks = sizeof(els_VmObj);
    L->GCthreshold = MAX_INT;
    L->allowhooks = 1;
    L->errorJmp = NULL;
    if (els_Heap_runprotected(L, f_elsopen, &stacksize) != 0){
        els_closevm(L);
        return NULL;
    }
    L->GCthreshold = 2 * L->nblocks;
    return L;
}

ELS_API void els_closevm(els_VmObj *L)
{
#ifdef ELS_VM_GC_ENABLE
    els_Gc_collect(L, 1);
#endif
    els_string_freeall(L);
    if (L->stack)
        L->nblocks -= (L->stack_last - L->stack + 1) * sizeof(StackObj);
    els_Mem_free(L, L->stack);
    L->nblocks -= (L->last_tag + 1) * sizeof(struct TM);
    els_Mem_free(L, L->UNITSEC);
    L->nblocks -= (L->refSize) * sizeof(struct Ref);
    els_Mem_free(L, L->refArray);
    L->nblocks -= (L->Mbuffsize) * sizeof(char);
    els_Mem_free(L, L->Mbuffer);
    els_Mem_free(L, L);
}
